﻿//-----------------------------------------------------------------------
// <copyright>
// This file is subject to the terms and conditions of the Microsoft Public License (MS-PL).
// See http://www.microsoft.com/opensource/licenses.mspx#Ms-PL for more details.
// </copyright>
//----------------------------------------------------------------------- 
using System;
using System.CodeDom.Compiler;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Fulcrum.UnitTests
{
    [TestClass]
    [DeploymentItem("Include.txt")]
    public class TextTemplatingEngineHostTests
    {
        public TextTemplatingEngineHostTests()
        {
            // nothing
        }

        TextTemplatingEngineHost host;
        MockGenerationDataContext dataContext;

        [TestInitialize]
        public void TestInitialize()
        {
            this.dataContext = new MockGenerationDataContext();
            this.host = new TextTemplatingEngineHost(this.dataContext);
        }

        [TestMethod]
        public void TestErrorsUnSet()
        {
            Assert.AreEqual(0, host.Errors.Count);
        }

        [TestMethod]
        public void TestErrors()
        {
            var errorCollection = CreateErrorCollection();
            this.host.LogErrors(errorCollection);

            Assert.AreEqual(2, host.Errors.Count);
            Assert.AreEqual("File.cs", host.Errors.ElementAt(0).FileName);
            Assert.AreEqual("File2.cs", host.Errors.ElementAt(1).FileName);
        }

        private static CompilerErrorCollection CreateErrorCollection()
        {
            var errorCollection = new CompilerErrorCollection();
            errorCollection.Add(new CompilerError("File.cs", 12, 23, "1231", "An error"));
            errorCollection.Add(new CompilerError("File2.cs", 12, 23, "1231", "An error"));

            return errorCollection;
        }

        [TestMethod]
        public void TestErrorsReadOnly()
        {
            var errorCollection = CreateErrorCollection();
            this.host.LogErrors(errorCollection);

            Assert.AreEqual(typeof(ReadOnlyCollection<CompilerError>), this.host.Errors.GetType());
        }

        [TestMethod]
        public void TestGetHostOptionValid()
        {
            var result = this.host.GetHostOption("CacheAssemblies");

            Assert.AreEqual(true, result);
        }

        [TestMethod]
        public void TestGetHostOptionInvalid()
        {
            var result = this.host.GetHostOption("blahblah");

            Assert.AreEqual(null, result);
        }

        [TestMethod]
        public void TestGetHostOptionNull()
        {
            var result = this.host.GetHostOption(null);

            Assert.AreEqual(null, result);
        }

        [TestMethod]
        public void TestGetHostOptionEmpty()
        {
            var result = this.host.GetHostOption("");

            Assert.AreEqual(null, result);
        }

        [TestMethod]
        public void TestLoadIncludeTest()
        {
            string content;
            string location;

            var result = this.host.LoadIncludeText("Include.txt", out content, out location);

            Assert.AreEqual(true, result);
            Assert.AreEqual(IncludeFileContent, content);
            Assert.AreEqual("", location);
        }

        const string IncludeFileContent = "this is \r\na multiline\r\ninclude file.";

        [TestMethod]
        public void TestLoadIncludeTestInvalid()
        {
            string content;
            string location;

            var result = this.host.LoadIncludeText("Invalid.txt", out content, out location);

            Assert.AreEqual(false, result);
            Assert.AreEqual("", content);
            Assert.AreEqual("", location);
        }

        [TestMethod]
        [ExpectedException(typeof(NullReferenceException))]
        public void TestLogErrorsNull()
        {
            this.host.LogErrors(null);

            Assert.Fail("an exception was expected");
        }

        [TestMethod]
        public void TestOutputFileExtensionNotSet()
        {
            Assert.AreEqual(null, this.host.OutputFileExtension);
        }

        [TestMethod]
        public void TestOutputFileExtension()
        {
            this.host.SetFileExtension("jpg");

            Assert.AreEqual("jpg", this.host.OutputFileExtension);
        }

        [TestMethod]
        public void TestProvideTemplatingAppDomain()
        {
            var appDomain = this.host.ProvideTemplatingAppDomain(null);

            Assert.AreEqual(AppDomain.CurrentDomain, appDomain);
        }

        [TestMethod]
        public void TestProvideTemplatingAppDomainDataContext()
        {
            Assert.AreNotSame(this.dataContext, GenerationDataContext.CurrentDataContext);

            var appDomain = this.host.ProvideTemplatingAppDomain(null);

            Assert.AreSame(this.dataContext, GenerationDataContext.CurrentDataContext);
        }

        [TestMethod]
        [ExpectedException(typeof(ApplicationException))]
        public void TestResolveDirectiveProcessor()
        {
            this.host.ResolveDirectiveProcessor(null);
        }

        [TestMethod]
        public void TestResolveParameterValue()
        {
            var result = this.host.ResolveParameterValue("one", "two", "three");

            Assert.AreEqual("", result);
        }

        [TestMethod]
        [ExpectedException(typeof(ArgumentNullException))]
        public void TestResolveParameterValueNullDirective()
        {
            var result = this.host.ResolveParameterValue(null, "two", "three");

            Assert.Fail("Exception expected");
        }

        [TestMethod]
        public void TestResolveParameterValueEmptyDirective()
        {
            var result = this.host.ResolveParameterValue(String.Empty, "two", "three");

            Assert.AreEqual("", result);
        }

        [TestMethod]
        [ExpectedException(typeof(ArgumentNullException))]
        public void TestResolveParameterValueNullProcessor()
        {
            var result = this.host.ResolveParameterValue("one", null, "three");

            Assert.Fail("Exception expected");
        }

        [TestMethod]
        public void TestResolveParameterValueEmptyProcessor()
        {
            var result = this.host.ResolveParameterValue("one", String.Empty, "three");

            Assert.AreEqual("", result);
        }

        [TestMethod]
        [ExpectedException(typeof(ArgumentNullException))]
        public void TestResolveParameterValueNullName()
        {
            var result = this.host.ResolveParameterValue("one", "two", null);

            Assert.Fail("Exception expected");
        }

        [TestMethod]
        public void TestResolveParameterValueEmptyName()
        {
            var result = this.host.ResolveParameterValue("one", "two", String.Empty);

            Assert.AreEqual("", result);
        }

        [TestMethod]
        [ExpectedException(typeof(NotImplementedException))]
        public void TestSetOutputEncoding()
        {
            this.host.SetOutputEncoding(Encoding.ASCII, true);
        }

        [TestMethod]
        public void TestStandardImports()
        {
            var result = this.host.StandardImports;

            Assert.AreEqual(1, result.Count);
            Assert.AreEqual("System", result.ElementAt(0));
        }

        [TestMethod]
        public void TestStandardAssemblyReferences()
        {
            var result = this.host.StandardAssemblyReferences;

            Assert.AreEqual(1, result.Count);

            bool endWith = result.ElementAt(0).EndsWith(@"\System.dll");
            Assert.IsTrue(endWith);
        }

        [TestMethod]
        public void TestResolveAssemblyReferenceFileExists()
        {
            var result = this.host.ResolveAssemblyReference("Include.txt");

            Assert.AreEqual("Include.txt", result);
        }

        [TestMethod]
        [ExpectedException(typeof(NullReferenceException))]
        public void TestResolveAssemblyReferenceNotFileExists()
        {
            this.host.TemplateFile = "File.txt";
            var result = this.host.ResolveAssemblyReference("badFile.dll");
        }

        [TestMethod]
        public void TestResolveAssemblyReferenceFileInTemplateDir()
        {
            var systemDir = Environment.SystemDirectory;
            this.host.TemplateFile = Path.Combine(systemDir, "ntdll.dll");
            var result = this.host.ResolveAssemblyReference("ntoskrnl.exe");

            Assert.AreEqual(Path.Combine(systemDir, "ntoskrnl.exe"), result);
        }

        [TestMethod]
        public void TestResolveAssemblyReferenceFileInGac()
        {
            this.host.TemplateFile = "File.txt";
            var result = this.host.ResolveAssemblyReference("System");

            bool endWith = result.EndsWith(@"\System.dll");
            Assert.IsTrue(endWith);
        }

        [TestMethod]
        public void TestResolvePathFileExists()
        {
            var result = this.host.ResolvePath("Include.txt");

            Assert.AreEqual("Include.txt", result);
        }

        [TestMethod]
        public void TestResolvePathNotFileExists()
        {
            this.host.TemplateFile = "File.txt";
            var result = this.host.ResolvePath("badFile.dll");

            Assert.AreEqual("badFile.dll", result);
        }

        [TestMethod]
        public void TestResolvePathFileInTemplateDir()
        {
            var systemDir = Environment.SystemDirectory;
            this.host.TemplateFile = Path.Combine(systemDir, "ntdll.dll");
            var result = this.host.ResolvePath("ntoskrnl.exe");

            Assert.AreEqual(Path.Combine(systemDir, "ntoskrnl.exe"), result);
        }
    }
}
